{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 范畴理论\n",
    "\n",
    "- 时间: 20220216\n",
    "- 撰写: tlw\n",
    "- 原文: https://docs.sympy.org/latest/modules/categories.html\n",
    "\n",
    "## 引言\n",
    "\n",
    "SymPy 的范畴理论模块将允许在单一范畴内操作图，包括在 TikZ 中绘制它们, 并决定它们是否可交换。\n",
    "\n",
    "这个模块试图遵循的一般参考Adamek, H. Herrlich. G. E. Strecker的论文《抽象与具体的分类-猫的乐趣》。\n",
    "\n",
    "这本书的最新版本应该可以从 http://katmat.math.uni-bremen.de/acc/acc.pdf 获取到, 该模块仍然处于胚胎前阶段。\n",
    "\n",
    "## 基类引用\n",
    "\n",
    "范畴理论中实现一些基本概念的类: 对象(object)、态射(morphisms)、范畴(categories)和图(diagram)。\n",
    "\n",
    "### 类 `class sympy.categories.Object(name, **assumptions)` \n",
    "\n",
    "Object是任何抽象范畴中对象的基类。\n",
    "\n",
    "> 虽然从技术上讲，`Basic` 的任何实例都可以做到基类，但`object`类是在抽象类别中创建抽象对象的推荐方法。\n",
    "\n",
    "### 类 `class sympy.categories.Morphism(domain, codomain)` \n",
    "\n",
    "Morphism是任何抽象范畴中态射的基类。\n",
    "\n",
    "> 在抽象范畴中，态射是位于两个范畴对象之间的箭头。箭头开始的对象称为`域`(`domain`)，箭头结束的对象称为`余域`(`codomain`)。同一对物体之间的两个态射被认为是相同的态射。为了区分同一对象之间的形态，可以使用`命名`(`NamedMorphism`)。\n",
    "\n",
    "> 禁止实例化`Morphism`类，实际会使用它的派生类, 请参考: 标识`IdentityMorphism`, 命名`NamedMorphism`, 组合`CompositeMorphism`\n",
    "\n",
    "#### 属性 `codomain` \n",
    "\n",
    "表示余域"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle B$"
      ],
      "text/plain": [
       "Object(\"B\")"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism\n",
    "\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "f.codomain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 方法 `compose(other)` \n",
    "\n",
    "组合, 用所提供的形态构成自我\n",
    "\n",
    "组合中元素的顺序通常的顺序, 例如: 构造 $g \\circ f$ 使用 g.compose(f)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle g\\circ f:A\\rightarrow C$"
      ],
      "text/plain": [
       "CompositeMorphism((NamedMorphism(Object(\"A\"), Object(\"B\"), \"f\"), NamedMorphism(Object(\"B\"), Object(\"C\"), \"g\")))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "g * f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle A$"
      ],
      "text/plain": [
       "Object(\"A\")"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(g * f).domain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle C$"
      ],
      "text/plain": [
       "Object(\"C\")"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(g * f).codomain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `domain` \n",
    "\n",
    "返回态射的领域"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle A$"
      ],
      "text/plain": [
       "Object(\"A\")"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "f.domain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `class sympy.categories.NamedMorphism(domain, codomain, name)` \n",
    "\n",
    "描述一个具有名字的态射\n",
    "\n",
    "名字被用于区分和辨识具有相同领域`domain`和余域`codomain`: 两个命名的态射都具有相同的域和余域就可以说他们等效的."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'f'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "f.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `class sympy.categories.CompositeMorphism(*components)` \n",
    "\n",
    "描述由多个态射组成的态射\n",
    "\n",
    "判定两个组合态射是否想等, 需要判断他们的构成部分是相同的且具有相同的次序.\n",
    "\n",
    "构建这个类的参数会被列举为图序: 要从`Morphism`的实例`g`和`f`, 获得 $ g \\circ f $, 需要使用`CompositeMorphism(f, g)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle g\\circ f:A\\rightarrow C$"
      ],
      "text/plain": [
       "CompositeMorphism((NamedMorphism(Object(\"A\"), Object(\"B\"), \"f\"), NamedMorphism(Object(\"B\"), Object(\"C\"), \"g\")))"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism, CompositeMorphism\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "g * f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "CompositeMorphism(f, g) == g * f"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `codomain` \n",
    "\n",
    "返回态射的余域\n",
    "\n",
    "组合态射的余域是最后一个组件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle C$"
      ],
      "text/plain": [
       "Object(\"C\")"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(g * f).codomain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `components` \n",
    "\n",
    "返回组合态射中所有的组件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left( f:A\\rightarrow B, \\  g:B\\rightarrow C\\right)$"
      ],
      "text/plain": [
       "(NamedMorphism(Object(\"A\"), Object(\"B\"), \"f\"), NamedMorphism(Object(\"B\"), Object(\"C\"), \"g\"))"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(g * f).components"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `domain` \n",
    "\n",
    "返回组合态射的域。组合态射的域是所有域中的第一个"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle A$"
      ],
      "text/plain": [
       "Object(\"A\")"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(g * f).domain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 方法 `flatten(new_name)` \n",
    "\n",
    "消除该态射的组合结构\n",
    "\n",
    "如果`new_name`为空, 返回一个带有生成名称的`NamedMorphism`, 否则返回一个`Morphism`。 这两种情况下, 新的态射的域都是这种符合态射的域, 而新态射的余域则是这种复合态射的余域。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle h:A\\rightarrow C$"
      ],
      "text/plain": [
       "NamedMorphism(Object(\"A\"), Object(\"C\"), \"h\")"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(g * f).flatten(\"h\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `class sympy.categories.IdentityMorphism(domain)` \n",
    "\n",
    "描述标识的态射。同一态射是具有相同的域和余域的态射，它在复合中起着同一的作用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import IdentityMorphism\n",
    "id_A = IdentityMorphism(A)\n",
    "id_B = IdentityMorphism(B)\n",
    "f * id_A == f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id_B * f == f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle id:A\\rightarrow A$"
      ],
      "text/plain": [
       "IdentityMorphism(Object(\"A\"))"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id_A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle id:B\\rightarrow B$"
      ],
      "text/plain": [
       "IdentityMorphism(Object(\"B\"))"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id_B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle f:A\\rightarrow B$"
      ],
      "text/plain": [
       "NamedMorphism(Object(\"A\"), Object(\"B\"), \"f\")"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "Uncomposable morphisms.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m/media/tlw/move/gitee/python-learn/sympy/category.ipynb Cell 28'\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> <a href='vscode-notebook-cell:/media/tlw/move/gitee/python-learn/sympy/category.ipynb#ch0000027?line=0'>1</a>\u001b[0m f \u001b[39m*\u001b[39;49m id_B\n",
      "File \u001b[0;32m~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py:143\u001b[0m, in \u001b[0;36mMorphism.__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=129'>130</a>\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m__mul__\u001b[39m(\u001b[39mself\u001b[39m, other):\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=130'>131</a>\u001b[0m     \u001b[39mr\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=131'>132</a>\u001b[0m \u001b[39m    Composes self with the supplied morphism.\u001b[39;00m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=132'>133</a>\u001b[0m \n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=140'>141</a>\u001b[0m \u001b[39m    compose\u001b[39;00m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=141'>142</a>\u001b[0m \u001b[39m    \"\"\"\u001b[39;00m\n\u001b[0;32m--> <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=142'>143</a>\u001b[0m     \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mcompose(other)\n",
      "File \u001b[0;32m~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py:128\u001b[0m, in \u001b[0;36mMorphism.compose\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=102'>103</a>\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mcompose\u001b[39m(\u001b[39mself\u001b[39m, other):\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=103'>104</a>\u001b[0m     \u001b[39mr\u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=104'>105</a>\u001b[0m \u001b[39m    Composes self with the supplied morphism.\u001b[39;00m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=105'>106</a>\u001b[0m \n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=125'>126</a>\u001b[0m \n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=126'>127</a>\u001b[0m \u001b[39m    \"\"\"\u001b[39;00m\n\u001b[0;32m--> <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=127'>128</a>\u001b[0m     \u001b[39mreturn\u001b[39;00m CompositeMorphism(other, \u001b[39mself\u001b[39;49m)\n",
      "File \u001b[0;32m~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py:309\u001b[0m, in \u001b[0;36mCompositeMorphism.__new__\u001b[0;34m(cls, *components)\u001b[0m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=305'>306</a>\u001b[0m         \u001b[39mraise\u001b[39;00m \u001b[39mTypeError\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39mAll components must be morphisms.\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=307'>308</a>\u001b[0m     \u001b[39mif\u001b[39;00m current\u001b[39m.\u001b[39mcodomain \u001b[39m!=\u001b[39m following\u001b[39m.\u001b[39mdomain:\n\u001b[0;32m--> <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=308'>309</a>\u001b[0m         \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39mUncomposable morphisms.\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=310'>311</a>\u001b[0m     normalised_components \u001b[39m=\u001b[39m CompositeMorphism\u001b[39m.\u001b[39m_add_morphism(\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=311'>312</a>\u001b[0m         normalised_components, current)\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=313'>314</a>\u001b[0m \u001b[39m# We haven't added the last morphism to the list of normalised\u001b[39;00m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/baseclasses.py?line=314'>315</a>\u001b[0m \u001b[39m# components.  Add it now.\u001b[39;00m\n",
      "\u001b[0;31mValueError\u001b[0m: Uncomposable morphisms."
     ]
    }
   ],
   "source": [
    "f * id_B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `class sympy.categories.Category(name, objects=EmptySet, commutative_diagrams=EmptySet)` \n",
    "\n",
    "（抽象）范畴\n",
    "\n",
    "例如范畴[JoyOfCats]是一个四元组 K = （O, hom, id, o) 符合:\n",
    "- 一组(假设的)类型O, 其成员被称作K-objects\n",
    "- 对K-objects中的每一对(A, B)，都有一组 hom(A, B), 其成员叫做A到B的K-morphisms\n",
    "- 对每一个K-objects中的A, 一个态射id: A -> A, 称作A的K-identity\n",
    "- 对于组合规则 `*` 联合每一个 `K-morphisms`中的`f: A->B` 和 `g: B->C`。 那么`g * f: A -> C`，称为`f`和`g`的组合。\n",
    "\n",
    "组合(composition)是联合(associative)的， `K-identities`是关于组合的恒等式，hom(A, B)的集合是成对且不相交的。\n",
    "\n",
    "这个类对它的对象和态射一无所知。抽象范畴的具体实例应该从这个范畴派生出来的类来实现。\n",
    "\n",
    "通过在构造函数中提供commutative_diagram_diagram, 可以断言图(`Diagram`)的某些实例在范畴(`Catagory`)中是可以交换的。\n",
    "\n",
    "> 参考: `Diagram`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy.categories import Object, NamedMorphism, Diagram, Category\n",
    "from sympy import FiniteSet\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "d = Diagram([f, g])\n",
    "K = Category(\"K\", commutative_diagrams=[d])\n",
    "K.commutative_diagrams == FiniteSet(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `commutative_diagrams`\n",
    "\n",
    "返回范畴中已知可交换`有限元`(`FiniteSet`)的`图示`(`Diagram`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = Diagram([f, g])\n",
    "K = Category(\"K\", commutative_diagrams=[d])\n",
    "K.commutative_diagrams == FiniteSet(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `name`\n",
    "\n",
    "返回范畴的名称。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy.categories import Category\n",
    "K = Category(\"K\")\n",
    "K.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `objects`\n",
    "\n",
    "返回此范畴的所有对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "K = Category(\"K\", FiniteSet(A, B))\n",
    "K.objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `class sympy.categories.Diagram(*args)`\n",
    "\n",
    "描述确定范畴的示图。\n",
    "\n",
    "通俗的解释, 示图`Diagram`是一个范畴`Category`的对象`object`和他们之间的某些态射`Morphisms`的集合。关于态射组合，一个示图仍然是一个幺半群`monoid`；也就是说，同一态射，以及图中所包含的所有态射的组合都属于该图。关于这个概念更正式的解释，请参见[Pare1970]。\n",
    "\n",
    "复合态射的组件也被添加到示图中，默认情况下这些态射没有赋予任何属性。\n",
    "\n",
    "交换示图通常伴随这样一句话:“如果存在此种性质的态射，那么具有此种性质的态射是存在的且示图是可交换的“。为了表示这一点，一个`Diagram`的实例包括了态射的集合，态射是前提和另一个结论的集合。前提和结论将属于相应范畴的态射与其性质的有限元`FinitSet`联系起来。\n",
    "\n",
    "符合态射的性质集合是其分支性质集合的交集。结论态射的域和余域应该是作为示图前提列出的态射的域和余域之一。\n",
    "\n",
    "没有检查所提供的对象和语法是否属于同一类别。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy import pprint, default_sort_key\n",
    "d = Diagram([f, g])\n",
    "premises_keys = sorted(d.premises.keys(), key=default_sort_key)\n",
    "pprint(premises_keys, use_unicode=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pprint(d.premises, use_unicode=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = Diagram([f, g], {g * f: \"unique\"})\n",
    "pprint(d.conclusions, use_unicode=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 参考 [Pare1970] B.Pareigis: 范畴与函子.学术出版社, 1970。\n",
    "\n",
    "#### 属性 `conculusions`\n",
    "\n",
    "返回设计图的推论"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy.categories import IdentityMorphism\n",
    "IdentityMorphism(A) in d.premises.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g * f in d.premises.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = Diagram([f, g], {g * f: \"unique\"})\n",
    "d.conclusions[g * f] == FiniteSet(\"unique\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 函数 `hom(A, B)`\n",
    "返回对象`A`和`B`之间的态射二元组集合，一个集合作为前提, 另外的一个集合作为态射的结论。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy import pretty\n",
    "print(pretty(d.hom(A, C), use_unicode=False))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 参考: `Object`, `Morphism`\n",
    "\n",
    "#### 方法 is_subdiagram(diagram)\n",
    "\n",
    "检查`diagram`是否是`self`的子图。如果`D‘`的所有前提(结论)都被包含于`D`，则可认为图`D'`是`D`的子图。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d1 = Diagram([f])\n",
    "d.is_subdiagram(d1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d1.is_subdiagram(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `objects`\n",
    "\n",
    "返回此图中出现在`FiniteSet`有限元中的所有对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d.objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `premises`\n",
    "\n",
    "返回图的所有前提。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = NamedMorphism(A, B, \"f\")\n",
    "id_A = IdentityMorphism(A)\n",
    "id_B = IdentityMorphism(B)\n",
    "d = Diagram([f])\n",
    "print(pretty(d.premises, use_unicode=False))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 方法 subdiagram_from_objects(objects)\n",
    "\n",
    "如果`objects`是`self`的子集，类似结论，该方法返回`self`所有前提`premises`的对象`objects`所包含的域和余域。属性被保留。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sympy.categories import Diagram\n",
    "from sympy import FiniteSet\n",
    "d = Diagram([f, g], {f: \"unique\", g*f: \"veryunique\"})\n",
    "d1 = d.subdiagram_from_objects(FiniteSet(A, B))\n",
    "d1 == Diagram([f], {f: \"unique\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 图的绘制\n",
    "\n",
    "本章节列举能够动态绘图的类。\n",
    "\n",
    "### 类 `sympy.categories.diagram_drawing.DiagramGrid(diagram, groups=None, **hints)`\n",
    "\n",
    "构造并在网格中绘制图。\n",
    "\n",
    "这个类的任务是分析所提供的图表的结构，并把它的对象放在一个网格上，这样，当对象和形态被实际绘制时，图表将是”可读的”，也就是说，不会有许多形态的交叉点。此类不执行任何实际的绘图。主要用于提供足够的元数据来绘制图表。\n",
    "\n",
    "一个简单的图: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy.categories import Diagram, DiagramGrid\n",
    "from sympy import pprint\n",
    "diagram = Diagram([f, g])\n",
    "grid = DiagramGrid(diagram)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A  B\n",
      "    \n",
      "   C\n"
     ]
    }
   ],
   "source": [
    "pprint(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "多数时候人们认为图是由逻辑组构成。可以通过使用`groups`关键字参数为`DiagramGroup`提供组的建议。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A  B  D\n",
      "       \n",
      "   C   \n"
     ]
    }
   ],
   "source": [
    "D = Object(\"D\")\n",
    "h = NamedMorphism(D, A, \"h\")\n",
    "k = NamedMorphism(D, B, \"k\")\n",
    "diagram = Diagram([f, g, h, k])\n",
    "grid = DiagramGrid(diagram)\n",
    "pprint(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，将通过将对象A和D分组使之彼此接近。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B     C\n",
      "       \n",
      "A  D   \n"
     ]
    }
   ],
   "source": [
    "grid = DiagramGrid(diagram, groups=[[A, D], B, C])\n",
    "pprint(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意其他对象的位置是如何变化的。\n",
    "可以使用参数向`DiagramGrid`的构造函数提供更多选项。当使用通用布局时，它看起来并不是线性的:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A  B   \n",
      "       \n",
      "   C  D\n",
      "       \n",
      "      E\n"
     ]
    }
   ],
   "source": [
    "E = Object(\"E\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "h = NamedMorphism(C, D, \"h\")\n",
    "i = NamedMorphism(D, E, \"i\")\n",
    "\n",
    "diagram = Diagram([f, g, h, i])\n",
    "grid = DiagramGrid(diagram)\n",
    "pprint(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用layout = \"secquential\"可以使之排成一行: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "C  B  A  D\n"
     ]
    }
   ],
   "source": [
    "grid = DiagramGrid(diagram, layout = \"sequential\")\n",
    "pprint(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时需要转置布局，虽然这个工作也可以手工完成，但是也可通过参数实现:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A\n",
      " \n",
      "B\n",
      " \n",
      "C\n",
      " \n",
      "D\n",
      " \n",
      "E\n"
     ]
    }
   ],
   "source": [
    "grid = DiagramGrid(diagram, layout=\"sequential\", transpose=True)\n",
    "pprint(grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还可以为每个组提供单独的提示。例如，参考`test/test_drawing.py`，查看五个引理[FiveLemma]的不同布局方式。\n",
    "\n",
    "> 参考资料: [FiveLemma] https://en.wikipedia.org/wiki/Five_lemma\n",
    "\n",
    "#### 属性 `height`\n",
    "\n",
    "返回此关系图布局中的行数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "diagram = Diagram([f, g])\n",
    "grid = DiagramGrid(diagram)\n",
    "grid.height"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 morphisms\n",
    "\n",
    "返回有足够意义可以绘制的态射(及其性质)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{NamedMorphism(Object(\"A\"), Object(\"B\"), \"f\"): EmptySet,\n",
       " NamedMorphism(Object(\"B\"), Object(\"C\"), \"g\"): EmptySet}"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "grid.morphisms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 属性 `width`\n",
    "\n",
    "返回关系图布局中的列数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "grid.width"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `class sympy.categories.diagram_drawing.ArrowStringDescription(unit, curving, curving_amount, looping_start, looping_end, horizontal_direction, vertical_direction, label_position, label)`\n",
    "\n",
    "存储生成箭头的`Xy-pic`描述所必需的信息。\n",
    "\n",
    "此类的主要目标是抽象出箭头的字符串表示形式，并提供产生实际 Xy-pic 字符串的功能。\n",
    "\n",
    "`unit`设置单位，将用于指定的数额曲线和其他距离。`horizontal_direction` 应该是一个`r`或`l`字符串，指定箭头的目标单元格相对于当前单元格的水平偏移量。`vertical_direction` 应使用一系列`d`或`u`指定垂直偏移量。`label_position` 应该是`^`、`_`或`|`，以指定标签应该位于箭头的上方、箭头的下方或箭头的正上方。注意，`above`和`below`这两个概念是相对于箭头方向的。标签存储形态标签。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\ar[dr]_{f}\n"
     ]
    }
   ],
   "source": [
    "from sympy.categories.diagram_drawing import ArrowStringDescription\n",
    "astr = ArrowStringDescription(\n",
    "    unit=\"mm\", curving=None, curving_amount=None,\n",
    "    looping_start=None, looping_end=None, horizontal_direction=\"d\",\n",
    "    vertical_direction=\"r\", label_position=\"_\", label=\"f\")\n",
    "print(str(astr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "弯曲应该是`^`，`_`之一，用来指定箭头将要弯曲的方向。弯曲量是一个描述多少单位的形态将要弯曲的数字:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\ar[dr]_{f}\n"
     ]
    }
   ],
   "source": [
    "astr = ArrowStringDescription(\n",
    "    unit=\"mm\", curving=None, curving_amount=12,\n",
    "    looping_start=None, looping_end=None, horizontal_direction=\"d\",\n",
    "    vertical_direction=\"r\", label_position=\"_\", label=\"f\")\n",
    "print(str(astr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "循环起`looping_start`始和循环结束`looping_end`目前仅用于循环态射，即具有相同的域和余域的态射。这两个属性应该存储一个有效的 Xy-pic 方向，并相应地指定箭头进入的方向和箭头返回的方向:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\ar@(u,l)[]_{f}\n"
     ]
    }
   ],
   "source": [
    "astr = ArrowStringDescription(\n",
    "    unit=\"mm\", curving=None, curving_amount=None,\n",
    "    looping_start=\"u\", looping_end=\"l\", horizontal_direction=\"\",\n",
    "    vertical_direction=\"\", label_position=\"_\", label=\"f\")\n",
    "print(str(astr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`label_displacement` 控制箭头标签离箭头两端的距离。例如，要将箭头标签放在箭头附近，请使用`>`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\ar@/^12mm/[dr]_>{f}\n"
     ]
    }
   ],
   "source": [
    "astr = ArrowStringDescription(\n",
    "    unit=\"mm\", curving=\"^\", curving_amount=12,\n",
    "    looping_start=None, looping_end=None, horizontal_direction=\"d\",\n",
    "    vertical_direction=\"r\", label_position=\"_\", label=\"f\")\n",
    "astr.label_displacement = \">\"\n",
    "print(str(astr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，箭头样式`arrow_style`用于指定箭头样式。要得到一个虚线箭头，例如，使用\"{->}”作为箭头样式:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\ar@/^12mm/@{-->}[dr]_{f}\n"
     ]
    }
   ],
   "source": [
    "astr = ArrowStringDescription(\n",
    "    unit=\"mm\", curving=\"^\", curving_amount=12,\n",
    "    looping_start=None, looping_end=None, horizontal_direction=\"d\",\n",
    "    vertical_direction=\"r\", label_position=\"_\", label=\"f\")\n",
    "astr.arrow_style = \"{-->}\"\n",
    "print(str(astr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将由 `XypicDiagramDrawer` 构建 `ArrowStringDescription` 实例，并提供给格式化程序中进一步使用。用户不需要自己构造 `ArrowStringDescription` 实例。\n",
    "\n",
    "为了能够正确使用这个类，鼓励读者检验 `Xy-pic` 用户指南，可在[Xypic]获得。\n",
    "\n",
    "> 参考资料: [Xypic] http://xy-pic.sourceforge.net/ "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `sympy.categories.diagram_drawing.XypicDiagramDrawer`\n",
    "\n",
    "给定一个 Diagram 和相应的 DiagramGrid，生成该图的 Xy-pic 表示形式。\n",
    "\n",
    "这个类中最重要的方法是画图。考虑下面的三角图, 为了绘制这个图，它的对象需要用一个 DiagramGrid 来布局。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[r]^{f} & B \\ar[d]^{g} \\\\\n",
      "& C \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism, Diagram\n",
    "from sympy.categories import DiagramGrid, XypicDiagramDrawer\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "diagram = Diagram([f, g], {g * f: \"unique\"})\n",
    "drawer = XypicDiagramDrawer()\n",
    "print(drawer.draw(diagram, grid))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有关详细信息，请参阅此方法的文档。\n",
    "\n",
    "为了控制箭头的外观，使用了格式化程序。字典箭头格式化`arrow_formatters`程序将变形映射到格式化程序函数。格式化程序接受 `ArrowStringDescription`，并允许修改由此公开的任何箭头属性。例如，要使所有具有唯一`unique`属性的态射都显示为虚线箭头，并将它们的名称前缀为`∃!`，应采取以下措施:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[r]^{f} & B \\ar[d]^{g} \\\\\n",
      "& C \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def formatter(astr):\n",
    "  astr.label = r\"\\exists !\" + astr.label\n",
    "  astr.arrow_style = \"{-->}\"\n",
    "drawer.arrow_formatters[\"unique\"] = formatter\n",
    "print(drawer.draw(diagram, grid))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "若要修改图表中所有箭头的外观，请设置 `default_arrow_formatter`。例如，为了使所有的形态标签离箭头稍微远一点，使它们看起来更集中，可以这样做:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[r]^(0.45){f} & B \\ar[d]^(0.45){g} \\\\\n",
      "& C \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def default_formatter(astr):\n",
    "  astr.label_displacement = \"(0.45)\"\n",
    "drawer.default_arrow_formatter = default_formatter\n",
    "print(drawer.draw(diagram, grid))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在一些图表中，一些态射被画成了曲线箭头:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[r]_{f} & B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_3mm/[ll]_{h} \\\\\n",
      "& C & \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "D = Object(\"D\")\n",
    "E = Object(\"E\")\n",
    "h = NamedMorphism(D, A, \"h\")\n",
    "k = NamedMorphism(D, B, \"k\")\n",
    "diagram = Diagram([f, g, h, k])\n",
    "grid = DiagramGrid(diagram)\n",
    "drawer = XypicDiagramDrawer()\n",
    "print(drawer.draw(diagram, grid))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了控制形态在默认情况下的弯曲程度，可以使用 `unit` 和 `default_curving_amount`属性:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[r]_{f} & B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_1cm/[ll]_{h} \\\\\n",
      "& C & \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "drawer.unit = \"cm\"\n",
    "drawer.default_curving_amount = 1\n",
    "print(drawer.draw(diagram, grid))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在一些图中，同一两个对象之间存在多个弯曲态射。为了控制这两个连续的形态之间的弯曲变化，使用默认的弯曲步骤:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[r]_{f} \\ar@/^1cm/[rr]^{h_{1}} & B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_2cm/[ll]_{h} \\\\\n",
      "& C & \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "drawer.default_curving_step = 1\n",
    "h1 = NamedMorphism(A, D, \"h1\")\n",
    "diagram = Diagram([f, g, h, k, h1])\n",
    "grid = DiagramGrid(diagram)\n",
    "print(drawer.draw(diagram, grid))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`default_curving_step` 的默认值是4个单位。\n",
    "\n",
    "> 参见: `draw`, `ArrowStringDescription`\n",
    "\n",
    "#### 方法 draw(diagram, grid, masked=None, diagram_format='')\n",
    "\n",
    "返回网格中图表的 Xy-pic 表示形式。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy.categories import Object, NamedMorphism, Diagram\n",
    "from sympy.categories import DiagramGrid, XypicDiagramDrawer\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "diagram = Diagram([f, g], {g * f: \"unique\"})\n",
    "grid = DiagramGrid(diagram)\n",
    "drawer = XypicDiagramDrawer()\n",
    "# 绘制这幅图\n",
    "print(drawer.draw(diagram, grid))\n",
    "# 隐藏的参数可以用来跳过图表中的态射\n",
    "print(drawer.draw(diagram, grid, masked=[g * f]))\n",
    "# 最后，可以使用 diagram_format 参数指定图的格式字符串。例如，为了增加1厘米的间距，按照以下步骤进行:\n",
    "print(drawer.draw(diagram, grid, diagram_format=\"@+1cm\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类 `sympy.categories.diagram_drawing.xypic_draw_diagram(diagram, masked=None, diagram_format='', groups=None, **hints)`\n",
    "\n",
    "提供组合 `DiagramGrid` 和 `XypicDiagramDrawer` 的快捷方式。返回一个 `Xy-pic` 图表示。参数`masked`是不会被绘制的态射列表。参数 `diagram_format` 格式是插入在 `xymatrix` 之后的格式字符串。组 `groups` 应该是一组逻辑组。这些提示 `hint` 将直接传递给 `DiagramGrid` 的构造器。\n",
    "\n",
    "有关参数的详细信息，请参阅 `DiagramGrid` 和 `XypicDiagramDrawer.draw` 的文档 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\\xymatrix{\n",
      "A \\ar[d]_{g\\circ f} \\ar[r]^{f} & B \\ar[ld]^{g} \\\\\n",
      "C & \n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism, Diagram\n",
    "from sympy.categories import xypic_draw_diagram\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "diagram = Diagram([f, g], {g * f: \"unique\"})\n",
    "print(xypic_draw_diagram(diagram))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 方法 `sympy.categories.diagram_drawing.preview_diagram(diagram, masked=None, diagram_format='', groups=None, output='png', viewer=None, euler=True, **hints)`\n",
    "\n",
    "结合 `xypic_draw_diagram` 和 `sympy.printing.preview` 的功能。参数 `mask`、 `diagram_format`、`group` 和 `hint`被传递给 `xypic_draw_diagram`，而 `output`、`viewer` 和 `euler` 则被传递给 `preview`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "latex program is not installed",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32m/media/tlw/move/gitee/python-learn/sympy/category.ipynb Cell 103'\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      <a href='vscode-notebook-cell:/media/tlw/move/gitee/python-learn/sympy/category.ipynb#ch0000118?line=6'>7</a>\u001b[0m g \u001b[39m=\u001b[39m NamedMorphism(B, C, \u001b[39m\"\u001b[39m\u001b[39mg\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m      <a href='vscode-notebook-cell:/media/tlw/move/gitee/python-learn/sympy/category.ipynb#ch0000118?line=7'>8</a>\u001b[0m d \u001b[39m=\u001b[39m Diagram([f, g], {g \u001b[39m*\u001b[39m f: \u001b[39m\"\u001b[39m\u001b[39munique\u001b[39m\u001b[39m\"\u001b[39m})\n\u001b[0;32m----> <a href='vscode-notebook-cell:/media/tlw/move/gitee/python-learn/sympy/category.ipynb#ch0000118?line=8'>9</a>\u001b[0m preview_diagram(d)\n",
      "File \u001b[0;32m~/.local/lib/python3.9/site-packages/sympy/categories/diagram_drawing.py:2592\u001b[0m, in \u001b[0;36mpreview_diagram\u001b[0;34m(diagram, masked, diagram_format, groups, output, viewer, euler, **hints)\u001b[0m\n\u001b[1;32m   <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/diagram_drawing.py?line=2588'>2589</a>\u001b[0m \u001b[39mfrom\u001b[39;00m \u001b[39msympy\u001b[39;00m\u001b[39m.\u001b[39;00m\u001b[39mprinting\u001b[39;00m \u001b[39mimport\u001b[39;00m preview\n\u001b[1;32m   <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/diagram_drawing.py?line=2589'>2590</a>\u001b[0m latex_output \u001b[39m=\u001b[39m xypic_draw_diagram(diagram, masked, diagram_format,\n\u001b[1;32m   <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/diagram_drawing.py?line=2590'>2591</a>\u001b[0m                                   groups, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mhints)\n\u001b[0;32m-> <a href='file:///~/.local/lib/python3.9/site-packages/sympy/categories/diagram_drawing.py?line=2591'>2592</a>\u001b[0m preview(latex_output, output, viewer, euler, (\u001b[39m\"\u001b[39;49m\u001b[39mxypic\u001b[39;49m\u001b[39m\"\u001b[39;49m,))\n",
      "File \u001b[0;32m~/.local/lib/python3.9/site-packages/sympy/printing/preview.py:246\u001b[0m, in \u001b[0;36mpreview\u001b[0;34m(expr, output, viewer, euler, packages, filename, outputbuffer, preamble, dvioptions, outputTexFile, **latex_settings)\u001b[0m\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=242'>243</a>\u001b[0m     shutil\u001b[39m.\u001b[39mcopyfile(join(workdir, \u001b[39m'\u001b[39m\u001b[39mtexput.tex\u001b[39m\u001b[39m'\u001b[39m), outputTexFile)\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=244'>245</a>\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m shutil\u001b[39m.\u001b[39mwhich(\u001b[39m'\u001b[39m\u001b[39mlatex\u001b[39m\u001b[39m'\u001b[39m):\n\u001b[0;32m--> <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=245'>246</a>\u001b[0m     \u001b[39mraise\u001b[39;00m \u001b[39mRuntimeError\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39mlatex program is not installed\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=247'>248</a>\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=248'>249</a>\u001b[0m     _check_output_no_window(\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=249'>250</a>\u001b[0m         [\u001b[39m'\u001b[39m\u001b[39mlatex\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m'\u001b[39m\u001b[39m-halt-on-error\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m'\u001b[39m\u001b[39m-interaction=nonstopmode\u001b[39m\u001b[39m'\u001b[39m,\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=250'>251</a>\u001b[0m          \u001b[39m'\u001b[39m\u001b[39mtexput.tex\u001b[39m\u001b[39m'\u001b[39m],\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=251'>252</a>\u001b[0m         cwd\u001b[39m=\u001b[39mworkdir,\n\u001b[1;32m    <a href='file:///~/.local/lib/python3.9/site-packages/sympy/printing/preview.py?line=252'>253</a>\u001b[0m         stderr\u001b[39m=\u001b[39mSTDOUT)\n",
      "\u001b[0;31mRuntimeError\u001b[0m: latex program is not installed"
     ]
    }
   ],
   "source": [
    "from sympy.categories import Object, NamedMorphism, Diagram\n",
    "from sympy.categories import preview_diagram\n",
    "A = Object(\"A\")\n",
    "B = Object(\"B\")\n",
    "C = Object(\"C\")\n",
    "f = NamedMorphism(A, B, \"f\")\n",
    "g = NamedMorphism(B, C, \"g\")\n",
    "d = Diagram([f, g], {g * f: \"unique\"})\n",
    "preview_diagram(d)"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "e7370f93d1d0cde622a1f8e1c04877d8463912d04d973331ad4851f04de6915a"
  },
  "kernelspec": {
   "display_name": "Python 3.9.5 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.5"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
